package cn.ecommerce.course.module.users.utils;

import cn.ecommerce.course.module.users.config.JwtConfig;
import cn.ecommerce.course.module.users.dal.dataobject.jwt.JwtPayload;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTPayload;
import cn.hutool.jwt.JWTUtil;

import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT工具类（优化版）
 * 功能：生成JWT令牌、验证令牌有效性并解析为实体类
 * 核心改进：解析结果返回JwtPayload实体类，支持通过getter方法安全获取字段
 */
@Component // 交给Spring容器管理，支持依赖注入
public class JwtUtils {

    /**
     * 注入JWT配置类
     * 用于获取配置文件中的密钥(secretKey)和过期时间(expireTime)
     */
    @Resource
    private JwtConfig jwtConfig;

    /**
     * 生成JWT令牌
     *
     * @param userId   自定义字段：用户ID，用于标识唯一用户
     * @param username 自定义字段：用户名，用于携带用户基本信息
     * @return 生成的JWT令牌字符串（格式：header.payload.signature）
     */
    public String generateToken(Long userId, String username) {
        // 构建payload数据（存储需要传递的信息）
        Map<String, Object> payload = new HashMap<>();

        // 存入自定义业务字段（需与JwtPayload实体类的字段对应）
        payload.put("userId", userId);       // 键"userId"需与实体类的userId字段对应
        payload.put("username", username);   // 键"username"需与实体类的username字段对应

        // 存入JWT标准声明字段
        payload.put(JWTPayload.ISSUED_AT, new Date());  // 签发时间（当前时间）
        // 过期时间 = 当前时间 + 配置文件中定义的过期毫秒数
        payload.put(JWTPayload.EXPIRES_AT, new Date(System.currentTimeMillis() + jwtConfig.getExpireTime()));

        // 使用Hutool工具生成令牌：自动处理头部和签名
        return JWTUtil.createToken(payload, jwtConfig.getSecretKey().getBytes());
    }

    /**
     * 验证JWT令牌并解析为实体类
     * 验证内容：签名正确性、是否在有效期内、格式合法性
     *
     * @param token 待验证的JWT令牌字符串
     * @return JwtPayload实体类（包含解析后的所有字段）；验证失败返回null
     */
    public JwtPayload verifyToken(String token) {
        try {
            // 1. 解析令牌并设置签名密钥（必须与生成时的密钥一致）
            JWT jwt = JWTUtil.parseToken(token).setKey(jwtConfig.getSecretKey().getBytes());

            // 2. 执行验证：自动校验签名和过期时间
            // 若验证失败（如签名错误、令牌过期），会抛出异常
            jwt.verify();

            // 3. 获取原始的Map格式payload数据
            Map<String, Object> payloadMap = jwt.getPayloads();

            // 4. 将Map转换为JwtPayload实体类（核心步骤）
            JwtPayload payload = new JwtPayload();

            // 4.1 处理自定义业务字段（注意类型转换）
            // 从Map中获取"userId"的值，转换为Long类型后设置到实体类
            Object userIdObj = payloadMap.get("userId");
            if (userIdObj != null) {
                // 兼容数值型字符串（如"123"）和Long类型
                payload.setUserId(Long.valueOf(userIdObj.toString()));
            }

            // 从Map中获取"username"的值，转换为String类型后设置到实体类
            Object usernameObj = payloadMap.get("username");
            if (usernameObj != null) {
                payload.setUsername(usernameObj.toString());
            }

            // 4.2 处理JWT标准声明字段（签发时间和过期时间）
            // 标准字段可能是Date类型或Long时间戳，需要兼容处理
            Object issuedAtObj = payloadMap.get(JWTPayload.ISSUED_AT);
            if (issuedAtObj instanceof Date) {
                // 若已是Date类型，直接赋值
                payload.setIssuedAt((Date) issuedAtObj);
            } else if (issuedAtObj instanceof Long) {
                // 若为Long时间戳，转换为Date类型
                payload.setIssuedAt(new Date((Long) issuedAtObj));
            }

            // 处理过期时间（与签发时间逻辑相同）
            Object expiresAtObj = payloadMap.get(JWTPayload.EXPIRES_AT);
            if (expiresAtObj instanceof Date) {
                payload.setExpiresAt((Date) expiresAtObj);
            } else if (expiresAtObj instanceof Long) {
                payload.setExpiresAt(new Date((Long) expiresAtObj));
            }

            // 5. 返回转换后的实体类
            return payload;

        } catch (Exception e) {
            // 捕获所有验证相关异常（如签名错误、令牌过期、格式错误等）
            // 实际项目中可添加日志记录：e.getMessage()
            return null; // 验证失败返回null
        }
    }
}
